import {
  AmcaxForm,
  BaseCommand,
  CommandDialog,
  ConstraintType,
  CurveType,
  DocumentEvents,
  EventsRes,
  ICmdDialogParams,
  ICurve,
  KEYBOARD_KEY,
} from '@amcax/base';
import { ctx2, userEvents } from '@app-cad/module';
import { Picker2D, PickerMgr } from '@amcax/renderer';
import { ReactComponent as Coincide } from '@app-cad/assets/svg-icons/constraints/coincide.svg';
import { ReactComponent as Collineation } from '@app-cad/assets/svg-icons/constraints/collineation.svg';
import { ReactComponent as Concentric } from '@app-cad/assets/svg-icons/constraints/concentric.svg';
import { ReactComponent as Equality } from '@app-cad/assets/svg-icons/constraints/equality.svg';
import { ReactComponent as Fixed } from '@app-cad/assets/svg-icons/constraints/fixed.svg';
import { ReactComponent as Horizontal } from '@app-cad/assets/svg-icons/constraints/horizontal.svg';
import { ReactComponent as Midpoint } from '@app-cad/assets/svg-icons/constraints/midpoint.svg';
import { ReactComponent as Parallel } from '@app-cad/assets/svg-icons/constraints/parallel.svg';
import { ReactComponent as Perpendicular } from '@app-cad/assets/svg-icons/constraints/perpendicular.svg';
import { ReactComponent as Tangency } from '@app-cad/assets/svg-icons/constraints/tangency.svg';
import { ReactComponent as Vertical } from '@app-cad/assets/svg-icons/constraints/vertical.svg';
import { ReactComponent as Symmetric } from '@app-cad/assets/svg-icons/constraints/symmetric.svg';
import { renderSketch } from '@app-cad/common/utils';
import { MessageTip } from '@app-cad/common/utils/MessageTip';
import { wsApi } from '@app-cad/server';
import { RJSFSchema, UiSchema } from '@rjsf/utils';
import validator from '@rjsf/validator-ajv8';
import { useMemoizedFn } from 'ahooks';
import i18n from 'i18next';
import { useEffect, useMemo, useState } from 'react';
import {
  SKETCH_CENTEREVENTS,
  sketchEventCenter,
} from '../../../events/SketchEventCenter';

const fullConstraintList = [
  ConstraintType.Horizontal,
  ConstraintType.Vertical,
  ConstraintType.Parallel,
  ConstraintType.Perpendicular,
  ConstraintType.Tangent,
  ConstraintType.Equal,
  ConstraintType.PointOnObject,
  ConstraintType.Fix,
  ConstraintType.Collinear,
  ConstraintType.Concentric,
  ConstraintType.MidPoint,
  ConstraintType.Symmetric,
];

function ConstraintCommand({ onClose, title }: ICmdDialogParams) {
  useEffect(() => {
    const onKeyUp = (e: EventsRes<KeyboardEvent>) => {
      if (e.key === KEYBOARD_KEY.ESC) {
        onClose();
      }
    };

    sketchEventCenter()
      .setState('mirror')
      .on(SKETCH_CENTEREVENTS.KEYUP, onKeyUp);

    return () => {
      sketchEventCenter().setDefault().off(SKETCH_CENTEREVENTS.KEYUP, onKeyUp);
    };
  }, [onClose]);

  const picker = PickerMgr.getPicker<Picker2D>('2d');
  const selectArray = picker.getSelectedObjectInfo();

  const sketchId = sketchEventCenter().getSketchInfo().sketchId;
  const [selectedData, setSelectedData] = useState(selectArray);
  const formItemLayout = {
    labelCol: { span: 3 },
    wrapperCol: { span: 21 },
  };

  const fixConstraintData = useMemo(() => {
    return (selectedData ?? [])
      .flatMap((c) => c.constraints)
      .filter((c) => c.type === ConstraintType.Fix);
  }, [selectedData]);

  const [allowedList, setAllowedList] = useState<
    { type: ConstraintType; isBatch: boolean }[]
  >([]);

  useEffect(() => {
    if (
      selectedData.length === 0 ||
      fixConstraintData.length === selectedData.length
    ) {
      return setAllowedList([]);
    }

    // 一共三种情况
    // 支持批量的单个约束，至少有一个就允许
    // 支持批量的关系约束，至少有两个才允许
    // 不支持批量的关系约束，需要有且仅有两个时允许

    // 非批量的场景
    const getNonBatchConstraints = () => {
      if (selectedData.length === 2) {
        if (fixConstraintData.length > 1) {
          return [];
        }
        const uniqueTypes = [...new Set(selectedData.map((item) => item.type))];
        // Line and curve

        const hasAxis = selectedData.some(
          ({ id }) => id === 'X-AXIS' || id === 'Y-AXIS',
        );

        if (
          uniqueTypes.length > 1 &&
          uniqueTypes.includes('Line') &&
          uniqueTypes.some(
            (type) =>
              type === 'Circle' ||
              type === 'Ellipse' ||
              type === 'ArcOfCircle' ||
              type === 'ArcOfEllipse',
          )
        ) {
          return [ConstraintType.Tangent];
          //line and point
        } else if (
          uniqueTypes.length > 1 &&
          uniqueTypes.includes('Line') &&
          uniqueTypes.includes('Point')
        ) {
          if (
            selectedData[0].relative.includes(selectedData[1].id) ||
            selectedData[1].relative.includes(selectedData[0].id)
          ) {
            return [];
          } else {
            return hasAxis
              ? [ConstraintType.PointOnObject]
              : [ConstraintType.PointOnObject, ConstraintType.MidPoint];
          }
        }
        // Line and line
        else if (uniqueTypes.length === 1 && uniqueTypes.includes('Line')) {
          return hasAxis
            ? []
            : [ConstraintType.Perpendicular, ConstraintType.Collinear];
        }
        // No lines
        else if (!uniqueTypes.includes('Line')) {
          if (
            uniqueTypes.length === 1 &&
            (uniqueTypes.includes('Ellipse') ||
              uniqueTypes.includes('ArcOfEllipse') ||
              uniqueTypes.includes('Circle') ||
              uniqueTypes.includes('ArcOfCircle'))
          ) {
            return [
              ConstraintType.Tangent,
              ConstraintType.Equal,
              ConstraintType.Concentric,
            ];
          } else if (
            (uniqueTypes.includes('Ellipse') ||
              uniqueTypes.includes('ArcOfEllipse')) &&
            (uniqueTypes.includes('Circle') ||
              uniqueTypes.includes('ArcOfCircle'))
          ) {
            return [ConstraintType.Tangent, ConstraintType.Concentric];
          } else if (
            uniqueTypes.includes('Circle') &&
            uniqueTypes.includes('ArcOfCircle')
          ) {
            return [ConstraintType.Tangent, ConstraintType.Concentric];
          } else if (
            uniqueTypes.length === 1 &&
            uniqueTypes.includes('Point')
          ) {
            return [
              ConstraintType.Horizontal,
              ConstraintType.Vertical,
              ConstraintType.PointOnObject,
            ];
          } else if (
            uniqueTypes.length > 1 &&
            uniqueTypes.includes('Point') &&
            uniqueTypes.some(
              (type) =>
                type === 'Circle' ||
                type === 'Ellipse' ||
                type === 'ArcOfEllipse' ||
                type === 'ArcOfCircle',
            )
          ) {
            return [ConstraintType.PointOnObject];
          } else {
            return [];
          }
        }
      }

      return [];
    };

    const getBatchConstraints = () => {
      const hasMultiSelect = selectedData.length > 1;
      if (fixConstraintData.length === selectedData.length) {
        return [];
      }
      if (selectedData.every((c) => c.type === 'Point') && hasMultiSelect) {
        return [
          ConstraintType.Horizontal,
          ConstraintType.Vertical,
          ConstraintType.PointOnObject,
          ConstraintType.Fix,
        ];
      }
      if (selectedData.every((c) => c.type === 'Line')) {
        const selectedAxis = selectedData.filter(
          (c) => c.id === 'X-AXIS' || c.id === 'Y-AXIS',
        );
        if (hasMultiSelect) {
          if (selectedAxis.length === 2) {
            return [];
          }

          if (selectedAxis.length === 1) {
            return [ConstraintType.Parallel, ConstraintType.Collinear];
          }
          return [
            ConstraintType.Horizontal,
            ConstraintType.Vertical,
            ConstraintType.Parallel,
            ConstraintType.Equal,
            ConstraintType.Collinear,
            ConstraintType.Fix,
          ];
        }
        return selectedAxis.length >= 1
          ? []
          : [
              ConstraintType.Horizontal,
              ConstraintType.Vertical,
              ConstraintType.Fix,
            ];
      }

      if (
        (selectedData.every(
          (c) => c.type === 'Circle' || c.type === 'ArcOfCircle',
        ) ||
          selectedData.every(
            (c) => c.type === 'Ellipse' || c.type === 'ArcOfEllipse',
          )) &&
        hasMultiSelect
      ) {
        return [
          ConstraintType.Concentric,
          ConstraintType.Equal,
          ConstraintType.Fix,
        ];
      }

      if (
        selectedData.every((c) =>
          ['Circle', 'ArcOfCircle', 'Ellipse', 'ArcOfEllipse'].includes(c.type),
        ) &&
        hasMultiSelect
      ) {
        return [ConstraintType.Concentric, ConstraintType.Fix];
      }

      return [ConstraintType.Fix];
    };

    const nonBatchConstraints = getNonBatchConstraints();
    const batchConstraints = getBatchConstraints().filter(
      (type) => !nonBatchConstraints.includes(type),
    );

    const result = [
      ...nonBatchConstraints.map((type) => ({ type, isBatch: false })),
      ...batchConstraints.map((type) => ({ type, isBatch: true })),
    ];

    if (selectedData.length === 3) {
      const { axis } = getSymmerticParams(selectedData);
      if (axis != null) {
        result.push({
          type: ConstraintType.Symmetric,
          isBatch: false,
        });
      }
    }

    setAllowedList(result);
  }, [selectedData]);

  const getSymmerticParams = (rawCurves: ICurve[]) => {
    if (rawCurves.every((c) => c.type === 'Line')) {
      return {
        axis: rawCurves[1],
        curves: [rawCurves[0], rawCurves[2]],
      };
    }

    let axis: ICurve;
    const curves: ICurve[] = [];

    rawCurves.forEach((c) => {
      if (axis == null && c.type === 'Line') {
        axis = c;
      } else {
        curves.push(c);
      }
    });

    const allowedTypes: CurveType[] = [
      'Line',
      'Point',
      'Circle',
      'ArcOfCircle',
      'Ellipse',
      'ArcOfEllipse',
    ];

    if (
      axis != null &&
      curves[0].type === curves[1].type &&
      curves.every((t) => allowedTypes.includes(t.type))
    ) {
      return {
        axis,
        curves,
      };
    }

    return {};
  };

  const btnDisable = allowedList.length === 0;

  const disabledList = fullConstraintList.filter((i) =>
    allowedList.every((v) => v.type !== i),
  );

  const onTreeViewSelected = useMemoizedFn(() => {
    setTimeout(() => {
      const selected = picker.getSelectedObjectInfo();
      setSelectedData(selected);
    });
  });

  useEffect(() => {
    sketchEventCenter()
      .setState('Constranit')
      .on(SKETCH_CENTEREVENTS.POINTERMOVE, mouseMove)
      .on(SKETCH_CENTEREVENTS.POINTERDOWN, mouseDown);
    userEvents.on(DocumentEvents.SELECT_TREE_OBJECT, onTreeViewSelected);

    return () => {
      sketchEventCenter()
        .off(SKETCH_CENTEREVENTS.POINTERMOVE, mouseMove)
        .off(SKETCH_CENTEREVENTS.POINTERDOWN, mouseDown)
        .setDefault();
      userEvents.off(DocumentEvents.SELECT_TREE_OBJECT, onTreeViewSelected);
    };
  }, []);

  const mouseMove = (e: EventsRes) => {
    picker.pick(e);
  };
  const mouseDown = (e: EventsRes) => {
    picker.select(e.event as PointerEvent, e.mouse);

    const selected = picker.getSelectedObjectInfo();
    setSelectedData(selected);

    if (!(selected?.length > 0)) {
      picker.clickNull();
    }
  };

  const schema: RJSFSchema = {
    type: 'object',
    properties: {
      Type: {
        type: 'number',
        oneOf: [
          {
            title: (
              <div className='constraintIcon-parent '>
                <Horizontal className='constraintIcon' />
                <span>水平</span>
              </div>
            ) as any as string,
            const: ConstraintType.Horizontal,
          },
          {
            title: (
              <div className='constraintIcon-parent'>
                <Vertical className='constraintIcon' />
                <span>竖直</span>
              </div>
            ) as any as string,
            const: ConstraintType.Vertical,
          },
          {
            title: (
              <div className='constraintIcon-parent'>
                <Parallel className='constraintIcon' />
                <span>平行</span>
              </div>
            ) as any as string,
            const: ConstraintType.Parallel,
          },
          {
            title: (
              <div className='constraintIcon-parent'>
                <Perpendicular className='constraintIcon' />
                <span>垂直</span>
              </div>
            ) as any as string,
            const: ConstraintType.Perpendicular,
          },
          {
            title: (
              <div className='constraintIcon-parent'>
                <Tangency className='constraintIcon' />
                <span>相切</span>
              </div>
            ) as any as string,
            const: ConstraintType.Tangent,
          },
          {
            title: (
              <div className='constraintIcon-parent'>
                <Collineation className='constraintIcon' />
                <span>共线</span>
              </div>
            ) as any as string,
            const: ConstraintType.Collinear,
          },
          {
            title: (
              <div className='constraintIcon-parent'>
                <Equality className='constraintIcon' />
                <span>相等</span>
              </div>
            ) as any as string,
            const: ConstraintType.Equal,
          },
          {
            title: (
              <div className='constraintIcon-parent'>
                <Concentric className='constraintIcon' />
                <span>同心</span>
              </div>
            ) as any as string,
            const: ConstraintType.Concentric,
          },
          {
            title: (
              <div className='constraintIcon-parent'>
                <Coincide className='constraintIcon' />
                <span>重合</span>
              </div>
            ) as any as string,
            const: ConstraintType.PointOnObject,
          },
          {
            title: (
              <div className='constraintIcon-parent'>
                <Fixed className='constraintIcon' />
                <span>固定</span>
              </div>
            ) as any as string,
            const: ConstraintType.Fix,
          },
          {
            title: (
              <div className='constraintIcon-parent'>
                <Midpoint className='constraintIcon' />
                <span>中点</span>
              </div>
            ) as any as string,
            const: ConstraintType.MidPoint,
          },
          {
            title: (
              <div className='constraintIcon-parent'>
                <Symmetric className='constraintIcon' />
                <span>对称</span>
              </div>
            ) as any as string,
            const: ConstraintType.Symmetric,
          },
        ],
      },
    },
  };
  const uiSchema: UiSchema = {
    Type: {
      'ui:widget': 'radio',
      'ui:classNames': 'cmd-sketch-form-constraint',
      'ui:enumDisabled': disabledList,
      'ui:label': '',
    },

    'ui:submitButtonOptions': {
      submitText: '执行',
      props: {
        disabled: btnDisable,
        className: 'btn',
        type: 'primary',
      },
    },
  };

  const onSubmit = async (data) => {
    //  两个点重合不能用点在对象上的约束类型
    let constraintType = data.formData.Type;

    const isBatch =
      allowedList.find((t) => t.type === constraintType)?.isBatch &&
      selectedData.length > 1;

    const submitSelectData = [...selectedData];

    if (submitSelectData.every((o) => o.type === 'Point')) {
      if (constraintType === ConstraintType.PointOnObject) {
        constraintType = ConstraintType.Coincident;
      }
    }
    if (
      submitSelectData.length === 2 &&
      !isBatch &&
      (submitSelectData[1].id === 'ORIGIN' ||
        submitSelectData[1].id === 'Y-AXIS' ||
        submitSelectData[1].id === 'X-AXIS')
    ) {
      submitSelectData.reverse();
    }

    if (
      fixConstraintData &&
      fixConstraintData.length > 0 &&
      !isBatch &&
      submitSelectData.length === 2
    ) {
      // 这里将有固定约束的放到首位
      if (fixConstraintData[0].target === submitSelectData[1].id) {
        submitSelectData.reverse();
      }
    }

    if (submitSelectData.length > 0) {
      if (constraintType === ConstraintType.Symmetric) {
        const { axis, curves } = getSymmerticParams(submitSelectData);
        const constraintData = await wsApi.createSketchConstraint(
          sketchId,
          curves[0].id,
          curves[1].id,
          constraintType,
          0,
          axis.id,
        );
        await renderSketch(sketchId, constraintData);
      } else if (!isBatch) {
        const constraintData = await wsApi.createSketchConstraint(
          sketchId,
          submitSelectData[0].id,
          submitSelectData.length > 1
            ? submitSelectData[1].id
            : submitSelectData[0].id,
          constraintType,
          0,
        );
        await renderSketch(sketchId, constraintData);
      } else {
        const constraintData = await wsApi.createBatchSketchConstraint(
          sketchId,
          submitSelectData.map((c) => c.id),
          constraintType,
        );
        await renderSketch(sketchId, constraintData);
      }
      ctx2.cadScene.assisGroup.clear();
      setSelectedData([]);
      picker.clickNull();
    } else {
      MessageTip.warning('请选择一个对象');
    }
  };
  return (
    <CommandDialog
      title={title}
      onClose={onClose}
      className='sketchDialog'
      placement='topLeft'
    >
      <AmcaxForm
        noValidate
        schema={schema}
        formContext={formItemLayout}
        uiSchema={uiSchema}
        validator={validator}
        onSubmit={onSubmit}
      />
    </CommandDialog>
  );
}

export const cmdConstranit: BaseCommand = {
  id: 'CmdPlaneRotate',
  label: 'command.constraint',
  icon: 'commands/sketch/geometric-constraint',
  group: 'sketch',
  create: (label, onClose) => (
    <ConstraintCommand
      title={i18n.t(label)}
      onClose={onClose}
    />
  ),
};
